/*
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 **
 **     http://www.apache.org/licenses/LICENSE-2.0
 **
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */

package org.geohunter.activity.map;

import org.geohunter.GeocacheFilter;
import org.geohunter.Geocache;
import org.geohunter.GeocacheList;
import org.geohunter.GeocacheListPrecomputed;
import org.geohunter.Toaster;
import org.geohunter.activity.map.DensityMatrix.DensityPatch;
import org.geohunter.database.CachesProviderDb;
import org.geohunter.task.DelayingTaskRunner;

import android.util.Log;

import java.util.List;

public class OverlayManager<OverlayClass> {
    //Environment
    private final OverlayClass mDensityOverlay;
    private final IMapView<OverlayClass> mGeoMapView;
    private final CachesProviderDb mCachesProviderDb;
    private final Toaster mTooManyCachesToaster;
    private final Toaster mDensityViewToaster;
    private final ICachePinsOverlayFactory<OverlayClass> mPinOverlayFactory;
    private final CacheHint mCacheHint;
    private final DensityOverlayDelegate mDensityOverlayDelegate;
    
    //Internal state
    private GeocacheList mPinList = GeocacheListPrecomputed.EMPTY;
    private boolean mUsesDensityMap;
    public boolean mIsTooManyCaches = false;
    private Geocache mSelected;
    public Area mArea;
    public GeocacheFilter mCacheFilter;
    private boolean mNeedsRefreshing = true;

    private final DelayingTaskRunner mOverlayCreatorRunner;
    
    public OverlayManager(IMapView<OverlayClass> geoMapView,
            OverlayClass densityOverlay, 
            boolean usesDensityMap,
            CachesProviderDb cachesProviderArea,
            GeocacheFilter cacheFilter, Toaster tooManyCachesToaster,
            Toaster densityViewToaster,
            ICachePinsOverlayFactory<OverlayClass> pinOverlayFactory,
            CacheHint cacheHint,
            DensityOverlayDelegate densityOverlayDelegate,
            DelayingTaskRunner overlayCreatorRunner) {
        mGeoMapView = geoMapView;
        mDensityOverlay = densityOverlay;
        mUsesDensityMap = usesDensityMap;
        mCachesProviderDb = cachesProviderArea;
        mCacheFilter = cacheFilter;
        mTooManyCachesToaster = tooManyCachesToaster;
        mPinOverlayFactory = pinOverlayFactory;
        mDensityViewToaster = densityViewToaster;
        mCacheHint = cacheHint;
        mOverlayCreatorRunner = overlayCreatorRunner;
        mDensityOverlayDelegate = densityOverlayDelegate;
    }

    public void initArea() {
        mArea = mGeoMapView.getVisibleArea();
        mNeedsRefreshing = true;
    }

    private OverlayCreatorTask<OverlayClass> makeWorker(Area area) {
        return new OverlayCreatorTask<OverlayClass>(this, mCachesProviderDb,
                mPinOverlayFactory, area, mCacheFilter, mSelected);
    }
    
    public Geocache getSelectedGeocache() {
        return mSelected;
    }
    
    public void setSelectedGeocache(Geocache geocache) {
        if (geocache == mSelected)
            return;
        mSelected = geocache;
        if (mUsesDensityMap)
            return;
        OverlayClass overlay = mPinOverlayFactory.makeOverlay(geocache, mPinList, this);
        mGeoMapView.setOverlay(overlay);
    }
    
    public void refreshIfNeeded() {
        if (!mNeedsRefreshing)
            return;
        mNeedsRefreshing = false;
        mOverlayCreatorRunner.abort();
        mGeoMapView.clearOverlay();
        mOverlayCreatorRunner.runTask(makeWorker(mArea));
    }
    
    public void invalidate() {
        mNeedsRefreshing = true;
    }

    public void setCacheFilter(GeocacheFilter cacheFilter) {
        mNeedsRefreshing = true;
        mCacheFilter = cacheFilter;
        mOverlayCreatorRunner.abort();
    }

    public void databaseChanged() {
        mNeedsRefreshing = true;
        mCachesProviderDb.notifyOfDbChange();
        mOverlayCreatorRunner.abort();
    }

    public void onScrollTo(Area area) {
        if (area.equals(mArea))
            return;
        
        if (!mIsTooManyCaches && !mUsesDensityMap && mArea.contains(area)) {
            //This is a zoomed in area, the old result is good
            return;
        }

        mOverlayCreatorRunner.runTask(makeWorker(area));
    }

    class PinOverlayResult implements Runnable {
        OverlayClass mOverlay;
        Area mNewArea;
        GeocacheList mList;
        public void run() {
            mGeoMapView.setOverlay(mOverlay);
            mUsesDensityMap = false;
            mIsTooManyCaches = false;
            mPinList = mList;
            mArea = mNewArea;
       }
    }
    class DensityPatchesResult implements Runnable {
        Area mNewArea;
        public List<DensityPatch> mDensityPatches;
        public void run() {
            mCacheHint.hide();
            mDensityOverlayDelegate.setPatches(mDensityPatches);
            mGeoMapView.setOverlay(mDensityOverlay);
            if (!mUsesDensityMap) {
                Log.d("geohunter", "Switching to density view");
                mDensityViewToaster.showToast();
            }
            mUsesDensityMap = true;
            mIsTooManyCaches = false;
            mArea = mNewArea;
        }
    }
    class TooManyCachesResult implements Runnable {
        Area mNewArea;
        public void run() {
            mCacheHint.hide();
            //mGeoMapView.clearOverlay();
            if (!mIsTooManyCaches) {
                mTooManyCachesToaster.showToast();
            }
            mIsTooManyCaches = true;
            //mArea = mNewArea;
        }
    }
}
